﻿using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;

namespace WPNextTools.NBase
{
    internal class WeakEeventHandler<TArgs> where TArgs : EventArgs
    {
        private WeakReference eventListener;
        private WeakReference eventSender;
        private string eventName;
        private EventInfo eventInfo;
        private Delegate eventDelegate;

        public WeakEeventHandler(object sender, IWeakEventListener eventListener, string eventName)
        {
            this.eventListener = new WeakReference(eventListener);
            this.eventSender = new WeakReference(sender);
            this.eventName = eventName;
            this.Update(sender, true);
        }

        public void Unsubscribe()
        {
            if (this.eventSender == null || !this.eventSender.IsAlive)
            {
                return;
            }
            this.Update(this.eventSender.Target, false);
            this.eventSender = null;
            this.eventInfo = null;
            this.eventDelegate = null;
        }

        private void Update(object sender, bool subscribe)
        {
            if (string.IsNullOrEmpty(eventName))
            {
                return;
            }
            if (NListSource.CollectionChangedEventName.Equals(eventName))
            {
                if (subscribe)
                {
                    (sender as INotifyCollectionChanged).CollectionChanged += OnCollectionChanged;
                    return;
                }
                (sender as INotifyCollectionChanged).CollectionChanged -= OnCollectionChanged;
            }
            if (subscribe)
            {
                this.eventInfo = sender.GetType().GetEvent(eventName);
                if (this.eventInfo != null)
                {
                    this.eventDelegate = Delegate.CreateDelegate(this.eventInfo.GetType(), this, "OnEvent");
                    this.eventInfo.AddEventHandler(sender, eventDelegate);
                    return;
                }
            }
            else
            {
                if (this.eventInfo != null)
                {
                    this.eventInfo.RemoveEventHandler(sender, eventDelegate);
                }
            }
        }

        private void OnEvent(object sender, TArgs e)
        {
            this.ProcessEvent(sender, e);
        }

        private void ProcessEvent(object sender, EventArgs e)
        {
            if (this.eventListener.IsAlive)
            {
                (this.eventListener.Target as IWeakEventListener).ReceiveEvent(sender, e);
                return;
            }
            this.Unsubscribe();
        }

        void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.ProcessEvent(sender, e);
        }
    }
}
